package cn.zwx.business.order.mq.producer;

import cn.zwx.business.order.service.OrderService;
import cn.zwx.order.req.CreateOrderReq;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @description: 订单的处理类
 * @projectName:rocket-mq-example
 * @see:cn.zwx.business.order.mq
 * @author:zhangwenxue
 * @createTime:2020/10/15 13:03
 * @version:1.0
 */
@Component(value = "transactionListenerImplForOrder")
public class TransactionListenerImplForOrder implements TransactionListener {

    private static final Logger logger =  LoggerFactory.getLogger(TransactionListenerImplForOrder.class);

    @Autowired
    private OrderService orderService;



    /**
     * @description 本地事务处理
     * @return org.apache.rocketmq.client.producer.LocalTransactionState
     * @author zhangwenxue
     * @createTime 2020/10/15 13:06
     **/
    @Override
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        logger.info("=========本地事务开始执行=============");
        String message = new String(msg.getBody());
        CreateOrderReq req = JSONObject.parseObject(message, CreateOrderReq.class);
        //模拟执行本地事务begin=======
        /**
         * 本地事务执行会有三种可能
         * 1、commit 成功
         * 2、Rollback 失败
         * 3、网络等原因服务宕机收不到返回结果
         */
        logger.info("本地事务执行参数:{}",req);
        int result = 2;
        try {
             result = orderService.createOrder(req);
        }catch (Exception e){
            //回滚过半消息,此时扣减库存不会生效
            logger.info("本地事务处理异常:{}",e.getMessage());
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
        logger.info("本地事务执行完毕:{}",result);
        //模拟执行本地事务end========
        //TODO 实际开发下面不需要我们手动返回，而是根据本地事务执行结果自动返回
        //1、二次确认消息，然后消费者可以消费
        if (result == 0) {
            //TODO 处理成功则插入数据库用户回查根据 key
            return LocalTransactionState.COMMIT_MESSAGE;
        }
        //2、回滚消息，Broker端会删除半消息
        if (result == 1) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
        //3、Broker端会进行回查消息
        if (result == 2) {
            return LocalTransactionState.UNKNOW;
        }
        return LocalTransactionState.COMMIT_MESSAGE;
    }

    /**
     * 只有上面接口返回 LocalTransactionState.UNKNOW 才会调用查接口被调用
     *
     * @param msg 消息
     * @return
     */
    @Override
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {
        logger.info("==========回查接口=========");
        String key = msg.getKeys();
        logger.info("接口回查返回参数:{}", JSON.toJSONString(new String(msg.getBody())));
        //TODO 1、必须根据key先去检查本地事务消息是否完成。
        /**
         * 因为有种情况就是：上面本地事务执行成功了，但是return LocalTransactionState.COMMIT_MESSAG的时候
         * 服务挂了，那么最终 Brock还未收到消息的二次确定，还是个半消息 ，所以当重新启动的时候还是回调这个回调接口。
         * 如果不先查询上面本地事务的执行情况 直接在执行本地事务，那么就相当于成功执行了两次本地事务了。
         */
        // TODO 2、这里返回要么commit 要么rollback。没有必要在返回 UNKNOW
        return LocalTransactionState.COMMIT_MESSAGE;
    }
}
